package com.hccl.mongoEntities;

import DM.DialogManager;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.common.io.Resources;
import com.hccl.mongoEntities.ProcessEntity;
import entities.JsonCommand;
import entities.Param;
import entities.StateNode;
//import entities.step.FunctionEntity;


import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by xuejunzhang on 2017/1/6.
 */

@JsonInclude(JsonInclude.Include.NON_NULL)
public class StateMachineModelEntity{

    //root state
    String stateNodePath = "";
    @JsonIgnore
    NavigationEntity root;
    //process list
    String processesPath = "";  //文件夹路径
    @JsonIgnore
     List<ProcessEntity> processes = new ArrayList<>();
    //params list
    String paramsPath = "";
    @JsonIgnore
     List<GrammarVariableEntity> params=new ArrayList<GrammarVariableEntity>();
    //config
    String configPath = "";
    @JsonIgnore
    ConfigEntity config;
    //api
    String apiPath = "";
    @JsonIgnore
    List<APIsEntity> apis = new ArrayList<>();
    @JsonIgnore
    public static Map<String ,NavigationEntity > stateNodeMap = new HashMap<String, NavigationEntity>();
    @JsonIgnore
    public static Map<String ,APIsEntity > apiMap = new HashMap<>();



    public StateMachineModelEntity(){

    }

//    private void checkStateParamMap(NavigationEntity stateNode){
//
//        Map<String, GrammarVariableEntity> statementParamMap=new HashMap<String, GrammarVariableEntity>();
//
//        for (GrammarVariableEntity param:params){
//            GrammarVariableEntity paramNew = param;
//            statementParamMap.put(param.getName(),paramNew);
//        }
//
//        for (String param:stateNode.getParams()){
//            GrammarVariableEntity paramAdd = statementParamMap.get(param);
//            if(paramAdd==null){
//                System.err.println("Param "+param+" in node"+stateNode.getId()+" did not exist in Model");
//                System.exit(-1);
//            }
//        }
//
//    }


    private void generateStateNodeMap(NavigationEntity stateNode){
        stateNodeMap.put(stateNode.getId(), stateNode);
//        checkStateParamMap(stateNode);

        if (stateNode.getSubStateNode() != null){
            for (NavigationEntity substate:stateNode.getSubStateNode())
                generateStateNodeMap(substate);
        }
    }

    private void generateApiMap(List<APIsEntity> apis){
        if (apis == null)
            return;
        for (APIsEntity functionEntity : apis){
            apiMap.put(functionEntity.getName(), functionEntity);
        }

    }



    @JsonCreator
    public StateMachineModelEntity(
            @JsonProperty("stateNodePath") String stateNodePath,
            @JsonProperty("processesPath") String processesPath,
            @JsonProperty("paramsPath") String paramsPath,
            @JsonProperty("configPath") String configPath,
            @JsonProperty("apiPath") String apiPath) {
        this.stateNodePath = stateNodePath != null ? stateNodePath : "";
        this.processesPath = processesPath != null ? processesPath : "";
        this.paramsPath = paramsPath != null ? paramsPath : "";
        this.configPath = configPath != null ? configPath : "";
        this.apiPath = apiPath != null ? apiPath : "";
        this.root = getModelFromInternelJson(stateNodePath,NavigationEntity.class);
        this.params = getListModelFromJson(paramsPath, GrammarVariableEntity.class);
        this.processes = generateProcesses(processesPath);
        this.config = getModelFromInternelJson(configPath, ConfigEntity.class);
        this.apis = getListModelFromJson(apiPath, APIsEntity.class);
        generateStateNodeMap(root);
        generateApiMap(apis);
    }

    private List<ProcessEntity> generateProcesses(String processesPath){
        List<ProcessEntity> ret = new ArrayList<>();
        if (processesPath.isEmpty())
            return ret;
        //String path = StateMachineModel.class.getClassLoader().getResource(processesPath).getPath();
        File file = new File(processesPath);
        if (file.exists() && file.isDirectory()){
            File[] files = file.listFiles();
            for (File file1 : files){
                ret.add(getModelFromInternelJson(processesPath+file1.getName(),ProcessEntity.class));
            }
        }
        return ret;
    }


    public static String readResource(ClassLoader cl, String path) {
        return readResource(cl, path, StandardCharsets.UTF_8);
    }

    public static String readResource(ClassLoader cl, String path, Charset charset) {
        try {
            //return Resources.toString(cl.getResource(path), charset);
            File file = new File(path);
            URL url = file.toURL();
//            URL url1 = cl.getResource(path);
//            String t = Resources.toString(url, charset);
            return Resources.toString(url, charset);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        return "";
    }



    public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(src);

        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        @SuppressWarnings("unchecked")
        List<T> dest = (List<T>) in.readObject();
        return dest;
    }




    public  static <T>  T getModelFromInternelJson(String jsonName,Class<T> t) {
        ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        try {
            //String path = readResource(mapper.getClass().getClassLoader(), jsonName);
            return mapper.readValue(readResource(mapper.getClass().getClassLoader(), jsonName), t);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
            return null;
        }
    }

    public  static <T>  List<T> getListModelFromJson(String jsonName,Class<T> t) {
        ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        try {
            JavaType javaType = mapper.getTypeFactory().constructParametricType(ArrayList.class, t);
            return mapper.readValue(readResource(mapper.getClass().getClassLoader(), jsonName), javaType);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
            return null;
        }
    }

    public NavigationEntity getRoot() {
        return root;
    }

    public void setRoot(NavigationEntity root) {
        this.root = root;
    }

    public List<GrammarVariableEntity> getParams() {
        return params;
    }

    public void setParams(List<GrammarVariableEntity> params) {
        this.params = params;
    }

    public List<ProcessEntity> getProcesses() {
        return processes;
    }

    public void setProcesses(List<ProcessEntity> processes) {
        this.processes = processes;
    }

    public String getConfigPath() {
        return configPath;
    }

    public void setConfigPath(String configPath) {
        this.configPath = configPath;
    }



    public String getParamsPath() {
        return paramsPath;
    }

    public void setParamsPath(String paramsPath) {
        this.paramsPath = paramsPath;
    }

    public String getProcessesPath() {
        return processesPath;
    }

    public void setProcessesPath(String processesPath) {
        this.processesPath = processesPath;
    }

    public String getStateNodePath() {
        return stateNodePath;
    }

    public void setStateNodePath(String stateNodePath) {
        this.stateNodePath = stateNodePath;
    }

    public ConfigEntity getConfig() {
        return config;
    }

    public void setConfig(ConfigEntity config) {
        this.config = config;
    }

    public String getApiPath() {
        return apiPath;
    }

    public void setApiPath(String apiPath) {
        this.apiPath = apiPath;
    }

    public List<APIsEntity> getApis() {
        return apis;
    }

    public void setApis(List<APIsEntity> apis) {
        this.apis = apis;
    }

    public String toString() {
        return "StateMachineModel" + root.toString();

    }

}
